package sg.edu.nus.iss.vmcs.store;

/*
 * Copyright 2003 ISS.
 * The contents contained in this document may not be reproduced in any
 * form or by any means, without the written permission of ISS, other
 * than for the purpose for which it has been supplied.
 *
 */

import java.io.*;

/**
 *This control object manages changes in Cash Store attributes and the Drinks Store attributes.
 *
 * @version 3.0 5/07/2003
 * @author Olivo Miotto, Pang Ping Li
 */

public class StoreController {

	private CashStore cStore;
	private DrinksStore dStore;

	private PropertyLoader cashLoader;
	private PropertyLoader drinksLoader;

	/**
	 * This consturctor creates an instance of the Store Controller. It will then create
	 * the Cash Store, Drinks Store and Door.
	 * @param cashLoader {@link sg.edu.nus.iss.vmcs.store.PropertyLoader}  
	 * @param drinksLoader {@link sg.edu.nus.iss.vmcs.store.PropertyLoader}  
	 */
	public StoreController(
		PropertyLoader cashLoader,
		PropertyLoader drinksLoader) {
		this.cashLoader = cashLoader;
		this.drinksLoader = drinksLoader;
	}

	/**
	 * This operation initiates the initialisation of the Drink Store and the Cash Store from data read-in form an input file.	
	 */
	public void initialize() throws IOException {
		cStore = new CashStore();
		dStore = new DrinksStore();
		initializeStores();
	}

	/**
	 * This operation initializes the Cash Store.	
	 */
	private void initializeStores() throws IOException {
		initializeCashStore();
		initializeDrinkStore();
	}

	/**
	 * This operation initializes the Drink Store.	 
	 */ 
	private void initializeDrinkStore() throws IOException {

		// get the drink file from the environment property file;
		int numOfItems = drinksLoader.getNumOfItems();
		dStore.setStoreSize(numOfItems);

		for (int i = 0; i < numOfItems; i++) {
            DrinksStoreItem item = (DrinksStoreItem) drinksLoader.getItem(i);
			StoreObject brand = item.getContent();
			StoreObject existingBrand = dStore.findObject(brand.getName());
			if (existingBrand != null) {
			    item.setContent(existingBrand);
			}
			dStore.addItem(i, item);
		}
	}

	/**
	 * This operation initializes the Cash Store.	 
	 */
	private void initializeCashStore() throws IOException {

		// get the cash file from the environment property file;
		int numOfItems = cashLoader.getNumOfItems();
		cStore.setStoreSize(numOfItems);

		for (int i = 0; i < numOfItems; i++) {
		    CashStoreItem item = (CashStoreItem) cashLoader.getItem(i);
			cStore.addItem(i, item);
		}
	}

	/**
	 * This operation will instruct the Cash Store to store the Coin sent as input, and 
	 * update the display on the Machinery Simulator Panel.  	 
	 */
	public void storeCoin(Coin c) {
		int idx = cStore.findCashStoreIndex(c);
		CashStoreItem item;
		item = (CashStoreItem) this.getStoreItem(Store.CASH, idx);
		item.increment();
	}

	/**
	 * This operation gets the size of the given Store type.
	 * @param type Store type constant defined in {@link sg.edu.nus.iss.vmcs.store.Store Store}
	 * @return Number of items in the store.
	 */
	public int getStoreSize(int type) {
		if (type == Store.CASH)
			return cStore.getStoreSize();
		else
			return dStore.getStoreSize();
	}

	/**
	 * This operation gets the content of the given Store type
	 *  as {@link sg.edu.nus.iss.vmcs.store.StoreItem StoreItem} array
	 * @param type Store type constant defined in {@link sg.edu.nus.iss.vmcs.store.Store Store}
	 * @return {@link sg.edu.nus.iss.vmcs.store.StoreItem StoreItem} array
	 */
	public StoreItem[] getStoreItems(int type) {
		if (type == Store.CASH)
			return cStore.getItems();
		else
			return dStore.getItems();
	}

	/**
	 * This operation will either:
	 * - instruct the Cash Store to update the quantity of a coin denomination to the new
	 * value supplied and update the total cash held in the Cash Store; or
	 * - instruct the Drinks Store to update the drinks stock for a drinks brand requested 
	 * to a new value supplied.
	 * @param type Store type constant defined in {@link sg.edu.nus.iss.vmcs.store.Store Store}
	 * @param idx Zero-based index of the Store Item 
	 * @param qty Quantity
	 */
	public void changeStoreQty(int type, int idx, int qty) {
			System.out.println(
				"StoreController.changeStoreQty: type:"
					+ type
					+ "   qty:"
					+ qty);
			if (type == Store.CASH)
				cStore.setQuantity(idx, qty);
			else
				dStore.setQuantity(idx, qty);
	}

	/**
	 * This operation will return the {@link sg.edu.nus.iss.vmcs.store.StoreItem StoreItem} of the given Store Type at the given index.
	 * @param type Store type constant defined in {@link sg.edu.nus.iss.vmcs.store.Store Store}
	 * @param idx Zero-based index of the {@link sg.edu.nus.iss.vmcs.store.StoreItem StoreItem}
	 * @return {@link sg.edu.nus.iss.vmcs.store.StoreItem StoreItem} at the given index of the given Store Type.
	 */
	public StoreItem getStoreItem(int type, int idx) {
		if (type == Store.CASH)
			return cStore.getStoreItem(idx);
		else
			return dStore.getStoreItem(idx);
	}

	/**
	 * This operation will set the price of {@link sg.edu.nus.iss.vmcs.store.DrinksBrand DrinksDrand} 
	 * at the given index of the {@link sg.edu.nus.iss.vmcs.store.DrinksStore DrinksStore}
	 * It will use the {@link sg.edu.nus.iss.vmcs.store.DrinksStoreItem#getContent() getContent()} method of the {@link sg.edu.nus.iss.vmcs.store.DrinksStoreItem DrinksStoreItem}
	 * to get the {@link sg.edu.nus.iss.vmcs.store.DrinksBrand DrinksBrand} and
	 * {@link sg.edu.nus.iss.vmcs.store.DrinksBrand#setPrice(int) setPrice(int)} method of the {@link sg.edu.nus.iss.vmcs.store.DrinksBrand DrinksBrand}
	 * to change the price. 
	 * @param idx Zero-based index of the {@link sg.edu.nus.iss.vmcs.store.DrinksStoreItem DrinksStoreItem}
	 * @param pr Price
	 */
	public void setPrice(int idx, int pr)  {
		DrinksStoreItem item;

		item = (DrinksStoreItem) dStore.getStoreItem(idx);
		DrinksBrand bd;

		bd = (DrinksBrand) item.getContent();

		bd.setPrice(pr);
	}

	/**
	 * This operation returns the total cash held in the {@link sg.edu.nus.iss.vmcs.store.CashStore Cash Store}.
	 * @return Total value of cash held in the {@link sg.edu.nus.iss.vmcs.store.CashStore Cash Store}
	 */
	public int getTotalCash()   {
		int i;
		int size;

		size = cStore.getStoreSize();
		CashStoreItem item;
		int qty;
		int val;
		int tc = 0;
		Coin c;

		for (i = 0; i < size; i++) {
			item = (CashStoreItem) cStore.getStoreItem(i);
			qty = item.getQuantity();
			c = (Coin) item.getContent();
			val = c.getValue();
			tc = tc + qty * val;
		}
		return tc;
	}

	/**
	 * This operation is to facilitate the transfer of all cash in {@link sg.edu.nus.iss.vmcs.store.CashStore Cash Store} to the 
	 * maintainer.
	 * @return Total number of coins transferred
	 */
	public int transferAll()  {
		int i;
		int cc = 0; // coin quauntity;
		int size = cStore.getStoreSize();

		CashStoreItem item;
		for (i = 0; i < size; i++) {
			item = (CashStoreItem) cStore.getStoreItem(i);
			cc = cc + item.getQuantity();
			item.setQuantity(0);
		}

		return cc;
	}

	/**
	 * This operation will close down the store management function of the vending
	 * machine. This involves saving the attributes of the stores to the property files.	 
	 */
	public void closeDown() throws IOException {
		// save back cash property;
		saveCashProperties();
        saveDrinksProperties();
	}

	/**
	 * This operation persists the {@link sg.edu.nus.iss.vmcs.store.CashStore Cash Store} information 
	 * when the simulation is ended.	
	 */
	private void saveCashProperties() throws IOException {
		int size = cStore.getStoreSize();
		cashLoader.setNumOfItems(size);
		for (int i = 0; i < size; i++) {
			cashLoader.setItem(i, cStore.getStoreItem(i));
		}
		cashLoader.saveProperty();
	}

	/**
	 * This operation persists the {@link sg.edu.nus.iss.vmcs.store.DrinksStore DrinksStore} information 
	 * when the simulation is ended.	
	 */
	private void saveDrinksProperties() throws IOException {
		int size = dStore.getStoreSize();
		drinksLoader.setNumOfItems(size);
		for (int i = 0; i < size; i++) {
			drinksLoader.setItem(i, dStore.getStoreItem(i));
		}
		drinksLoader.saveProperty();
	}

	/**
	 * This operation instructs the Drinks Store to dispense one drink, and then
	 * updates the Machinery Simulator Panel. It returns TRUE or FALSE to indicate
	 * whether dispensing was successful.
	 * @param idx Zero-based index of the Drink slot
	 */
	public void dispenseDrink(int idx)  {
		DrinksStoreItem item;
		item = (DrinksStoreItem) getStoreItem(Store.DRINK, idx);
		item.decrement();
	}

	/**
	 * This operation return the {@link sg.edu.nus.iss.vmcs.store.Store Store} 
	 * based on the given Store type.  
	 * @param type Store type constant defined in {@link sg.edu.nus.iss.vmcs.store.Store Store}
	 * @return {@link sg.edu.nus.iss.vmcs.store.Store Store}
	 */
	public Store getStore(int type) {
		if (type == Store.CASH)
			return (Store) cStore;
		else
			return (Store) dStore;
	}

	/**
	 * This operation instructs the Cash Store to issue a number of coins of a specific
	 * denomination, and then updates the Machinery Simulator Panel. It returns TRUE
	 * or FALSE to indicate whether the change issue was successful.
	 * @param idx Zero-based index of denomination held in Cash Store
	 * @param numOfCoins Number of coins to deduct
	 */
	public void giveChange(int idx, int numOfCoins)  {
		CashStoreItem item;
		item = (CashStoreItem) getStoreItem(Store.CASH, idx);
		for (int i = 0; i < numOfCoins; i++)
			item.decrement();
	}
}
